<?php if(!defined("BASEPATH")){ exit("No direct script access allowed"); }

        /**
         * Multi-Upload
         *
         * Extends CodeIgniters native Upload class to add support for multiple
         * uploads.
         *
         * @package                CodeIgniter
         * @subpackage        Libraries
         * @category        Uploads
         * @author                Conveyor Group <steven@conveyorgroup.com>
         * @link                https://github.com/stvnthomas/CodeIgniter-2.1-Multi-Upload
         */
                class MY_Upload extends CI_Upload {
                        
                        
                        /**
                         * Properties
                         */
                                 protected $_multi_upload_data                        = array();
                                protected $_multi_file_name_override        = "";
                                
                                
                        /**
                         * Initialize preferences
                         *
                         * @access        public
                         * @param        array
                         * @return        void
                         */
                                public function initialize($config = array()){
                                        //Upload default settings.
                                        $defaults = array(
                                                                        "max_size"                        => 0,
                                                                        "max_width"                        => 0,
                                                                        "max_height"                => 0,
                                                                        "max_filename"                => 0,
                                                                        "allowed_types"                => "",
                                                                        "file_temp"                        => "",
                                                                        "file_name"                        => "",
                                                                        "orig_name"                        => "",
                                                                        "file_type"                        => "",
                                                                        "file_size"                        => "",
                                                                        "file_ext"                        => "",
                                                                        "upload_path"                => "",
                                                                        "overwrite"                        => FALSE,
                                                                        "encrypt_name"                => FALSE,
                                                                        "is_image"                        => FALSE,
                                                                        "image_width"                => "",
                                                                        "image_height"                => "",
                                                                        "image_type"                => "",
                                                                        "image_size_str"        => "",
                                                                        "error_msg"                        => array(),
                                                                        "mimes"                                => array(),
                                                                        "remove_spaces"                => TRUE,
                                                                        "xss_clean"                        => FALSE,
                                                                        "temp_prefix"                => "temp_file_",
                                                                        "client_name"                => ""
                                                                );
                                        
                                        //Set each configuration.
                                        foreach($defaults as $key => $val){
                                                if(isset($config[$key])){
                                                        $method = "set_{$key}";
                                                        if(method_exists($this, $method)){
                                                                $this->$method($config[$key]);
                                                        } else {
                                                                $this->$key = $config[$key];
                                                        }
                                                } else {
                                                        $this->$key = $val;
                                                }
                                        }
                                        
                                        //Check if file_name was provided.
                                        if(!empty($this->file_name)){
                                                //Multiple file upload.
                                                if(is_array($this->file_name)){
                                                        //Clear file name override.
                                                        $this->_file_name_override = "";
                                                        
                                                        //Set multiple file name override.
                                                        $this->_multi_file_name_override = $this->file_name;
                                                //Single file upload.
                                                } else {
                                                        //Set file name override.
                                                        $this->_file_name_override = $this->file_name;
                                                        
                                                        //Clear multiple file name override.
                                                        $this->_multi_file_name_override = "";
                                                }
                                        }
                                }
                                
                                
                        /**
                         * File MIME Type
                         *
                         * Detects the (actual) MIME type of the uploaded file, if possible.
                         * The input array is expected to be $_FILES[$field].
                         *
                         * In the case of multiple uploads, a optional second argument may be
                         * passed specifying which array element of the $_FILES[$field] array
                         * elements should be referenced (name, type, tmp_name, etc).
                         *
                         * @access        protected
                         * @param        $file        array
                         * @param        $count        int
                         * @return        void
                         */
                                protected function _file_mime_type($file, $count=0){
                                        //Mutliple file?
                                        if(is_array($file["name"])){
                                                $tmp_name = $file["tmp_name"][$count];
                                                $type = $file["type"][$count];
                                        //Single file.
                                        } else {
                                                $tmp_name = $file["tmp_name"];
                                                $type = $file["type"];
                                        }
                                        
                                        //We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii).
                                        $regexp = "/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/";
                                        
                                        /* Fileinfo Extension - most reliable method.
                                         *
                                         * Unfortunately, prior to PHP 5.3 - it's only available as a PECL extension and the
                                         * more convenient FILEINFO_MIME_TYPE flag doesn't exist.
                                         */
                                                 if(function_exists("finfo_file")){
                                                         $finfo = finfo_open(FILEINFO_MIME);
                                                        if(is_resource($finfo)){
                                                                $mime = @finfo_file($finfo, $tmp_name);
                                                                finfo_close($finfo);
                                                                
                                                                /* According to the comments section of the PHP manual page,
                                                                 * it is possible that this function returns an empty string
                                                                 * for some files (e.g. if they don't exist in the magic MIME database).
                                                                 */
                                                                         if(is_string($mime) && preg_match($regexp, $mime, $matches)){
                                                                                 $this->file_type = $matches[1];
                                                                                return;
                                                                         }
                                                        }
                                                 }
                                                
                                        /* This is an ugly hack, but UNIX-type systems provide a "native" way to detect the file type,
                                         * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it
                                         * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better
                                         * than mime_content_type() as well, hence the attempts to try calling the command line with
                                         * three different functions.
                                         *
                                         * Notes:
                                         *        - the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
                                         *        - many system admins would disable the exec(), shell_exec(), popen() and similar functions
                                         *         due to security concerns, hence the function_exists() checks
                                         */
                                                 if(DIRECTORY_SEPARATOR !== "\\"){
                                                         $cmd = "file --brief --mime ".escapeshellarg($tmp_name)." 2>&1";
                                                        
                                                        if(function_exists("exec")){
                                                                /* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
                                                                 * However, we only neeed the last line, which is the actual return value of exec(), and as such - it overwrites
                                                                 * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy
                                                                 * value, which is only put to allow us to get the return status code.
                                                                 */
                                                                        $mime = @exec($cmd, $mime, $return_status);
                                                                        if($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches)){
                                                                                $this->file_type = $matches[1];
                                                                                return;
                                                                        }
                                                        }
                                                 }
                                                
                                                if((bool)@ini_get("safe_mode") === FALSE && function_exists("shell_exec")){
                                                        $mime = @shell_exec($cmd);
                                                        if(strlen($mime) > 0){
                                                                $mime = explode("\n", trim($mime));
                                                                if(preg_match($regexp, $mime[(count($mime) - 1)], $matches)){
                                                                        $this->file_type = $matches[1];
                                                                        return;
                                                                }
                                                        }
                                                }
                                                
                                                if(function_exists("popen")){
                                                        $proc = @popen($cmd, "r");
                                                        if(is_resource($proc)){
                                                                $mime = @fread($proc, 512);
                                                                @pclose($proc);
                                                                if($mime !== FALSE){
                                                                        $mime = explode("\n", trim($mime));
                                                                        if(preg_match($regexp, $mime[(count($mime) - 1)], $matches)){
                                                                                $this->file_type = $matches[1];
                                                                                return;
                                                                        }
                                                                }
                                                        }
                                                }
                                                
                                                //Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]["type"])
                                                if(function_exists("mime_content_type")){
                                                        $this->file_type = @mime_content_type($tmp_name);
                                                        //It's possible that mime_content_type() returns FALSE or an empty string.
                                                        if(strlen($this->file_type) > 0){
                                                                return;
                                                        }
                                                }
                                                
                                                //If all else fails, use $_FILES default mime type.
                                                $this->file_type = $type;
                                }
                                
                                
                        /**
                         * Set Multiple Upload Data
                         *
                         * @access        protected
                         * @return        void
                         */
                                protected function set_multi_upload_data(){
                                        $this->_multi_upload_data[] = array(
                                                "file_name"                        => $this->file_name,
                                                "file_type"                        => $this->file_type,
                                                "file_path"                        => $this->upload_path,
                                                "full_path"                        => $this->upload_path.$this->file_name,
                                                "raw_name"                        => str_replace($this->file_ext, "", $this->file_name),
                                                "orig_name"                        => $this->orig_name,
                                                "client_name"                => $this->client_name,
                                                "file_ext"                        => $this->file_ext,
                                                "file_size"                        => $this->file_size,
                                                "is_image"                        => $this->is_image(),
                                                "image_width"                => $this->image_width,
                                                "image_height"                => $this->image_height,
                                                "image_type"                => $this->image_type,
                                                "image_size_str"        => $this->image_size_str
                                        );
                                }
                                
                                
                        /**
                         * Get Multiple Upload Data
                         *
                         * @access        public
                         * @return        array
                         */
                                public function get_multi_upload_data(){
                                        return $this->_multi_upload_data;
                                }
                                
                                
                        /**
                         * Multile File Upload
                         *
                         * @access        public
                         * @param        string
                         * @return        mixed
                         */
                                public function do_multi_upload($field){
                                        //Is $_FILES[$field] set? If not, no reason to continue.
                                        if(!isset($_FILES[$field])){ return false; }
                                        
                                        //Is this really a multi upload?
                                        if(!is_array($_FILES[$field]["name"])){
                                                //Fallback to do_upload method.
                                                return $this->do_upload($field);
                                        }
                                        
                                        //Is the upload path valid?
                                        if(!$this->validate_upload_path()){
                                                //Errors will already be set by validate_upload_path() so just return FALSE
                                                return FALSE;
                                        }
                                        
                                        //Every file will have a separate entry in each of the $_FILES associative array elements (name, type, etc).
                                        //Loop through $_FILES[$field]["name"] as representative of total number of files. Use count as key in
                                        //corresponding elements of the $_FILES[$field] elements.
                                        for($i=0; $i<count($_FILES[$field]["name"]); $i++){
                                                //Was the file able to be uploaded? If not, determine the reason why.
                                                if(!is_uploaded_file($_FILES[$field]["tmp_name"][$i])){
                                                        //Determine error number.
                                                        $error = (!isset($_FILES[$field]["error"][$i])) ? 4 : $_FILES[$field]["error"][$i];
                                                        
                                                        //Set error.
                                                        switch($error){
                                                                //UPLOAD_ERR_INI_SIZE
                                                                case 1:
                                                                        $this->set_error("upload_file_exceeds_limit");
                                                                break;
                                                                //UPLOAD_ERR_FORM_SIZE
                                                                case 2:
                                                                        $this->set_error("upload_file_exceeds_form_limit");
                                                                break;
                                                                //UPLOAD_ERR_PARTIAL
                                                                case 3:
                                                                        $this->set_error("upload_file_partial");
                                                                break;
                                                                //UPLOAD_ERR_NO_FILE
                                                                case 4:
                                                                        $this->set_error("upload_no_file_selected");
                                                                break;
                                                                //UPLOAD_ERR_NO_TMP_DIR
                                                                case 6:
                                                                        $this->set_error("upload_no_temp_directory");
                                                                break;
                                                                //UPLOAD_ERR_CANT_WRITE
                                                                case 7:
                                                                        $this->set_error("upload_unable_to_write_file");
                                                                break;
                                                                //UPLOAD_ERR_EXTENSION
                                                                case 8:
                                                                        $this->set_error("upload_stopped_by_extension");
                                                                break;
                                                                default:
                                                                        $this->set_error("upload_no_file_selected");
                                                                break;
                                                        }
                                                        
                                                        //Return failed upload.
                                                        return FALSE;
                                                }
                                                
                                                //Set current file data as class variables.
                                                $this->file_temp        = $_FILES[$field]["tmp_name"][$i];
                                                $this->file_size        = $_FILES[$field]["size"][$i];
                                                $this->_file_mime_type($_FILES[$field], $i);
                                                $this->file_type        = preg_replace("/^(.+?);.*$/", "\\1", $this->file_type);
                                                $this->file_type        = strtolower(trim(stripslashes($this->file_type), '"'));
                                                $this->file_name        = $this->_prep_filename($_FILES[$field]["name"][$i]);
                                                $this->file_ext                = $this->get_extension($this->file_name);
                                                $this->client_name        = $this->file_name;
                                                
                                                //Is the file type allowed to be uploaded?
                                                if(!$this->is_allowed_filetype()){
                                                        $this->set_error("upload_invalid_filetype");
                                                        return FALSE;
                                                }
                                                
                                                //If we're overriding, let's now make sure the new name and type is allowed.
                                                //Check if a filename was supplied for the current file. Otherwise, use it's given name.
                                                if(!empty($this->_multi_file_name_override[$i])){
                                                        $this->file_name = $this->_prep_filename($this->_multi_file_name_override[$i]);
                                                        
                                                        //If no extension was provided in the file_name config item, use the uploaded one.
                                                        if(strpos($this->_multi_file_name_override[$i], ".") === FALSE){
                                                                $this->file_name .= $this->file_ext;
                                                        //An extension was provided, lets have it!
                                                        } else {
                                                                $this->file_ext = $this->get_extension($this->_multi_file_name_override[$i]);
                                                        }
                                                        
                                                        if(!$this->is_allowed_filetype(TRUE)){
                                                                $this->set_error("upload_invalid_filetype");
                                                                return FALSE;
                                                        }
                                                }
                                                
                                                //Convert the file size to kilobytes.
                                                if($this->file_size > 0){
                                                        $this->file_size = round($this->file_size/1024, 2);
                                                }
                                                
                                                //Is the file size within the allowed maximum?
                                                if(!$this->is_allowed_filesize()){
                                                        $this->set_error("upload_invalid_filesize");
                                                        return FALSE;
                                                }
                                                
                                                //Are the image dimensions within the allowed size?
                                                //Note: This can fail if the server has an open_basdir restriction.
                                                if(!$this->is_allowed_dimensions()){
                                                        $this->set_error("upload_invalid_dimensions");
                                                        return FALSE;
                                                }
                                                
                                                //Sanitize the file name for security.
                                                $this->file_name = $this->clean_file_name($this->file_name);
                                                
                                                //Truncate the file name if it's too long
                                                if($this->max_filename > 0){
                                                        $this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
                                                }
                                                
                                                //Remove white spaces in the name
                                                if($this->remove_spaces == TRUE){
                                                        $this->file_name = preg_replace("/\s+/", "_", $this->file_name);
                                                }
                                                
                                                /* Validate the file name
                                                 * This function appends an number onto the end of
                                                 * the file if one with the same name already exists.
                                                 * If it returns false there was a problem.
                                                 */
                                                        $this->orig_name = $this->file_name;
                                                        if($this->overwrite == FALSE){
                                                                $this->file_name = $this->set_filename($this->upload_path, $this->file_name);
                                                                if($this->file_name === FALSE){
                                                                        return FALSE;
                                                                }
                                                        }
                                                        
                                                /* Run the file through the XSS hacking filter
                                                 * This helps prevent malicious code from being
                                                 * embedded within a file. Scripts can easily
                                                 * be disguised as images or other file types.
                                                 */
                                                        if($this->xss_clean){
                                                                if($this->do_xss_clean() === FALSE){
                                                                        $this->set_error("upload_unable_to_write_file");
                                                                        return FALSE;
                                                                }
                                                        }
                                                        
                                                /* Move the file to the final destination
                                                 * To deal with different server configurations
                                                 * we'll attempt to use copy() first. If that fails
                                                 * we'll use move_uploaded_file(). One of the two should
                                                 * reliably work in most environments
                                                 */
                                                        if(!@copy($this->file_temp, $this->upload_path.$this->file_name)){
                                                                if(!@move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name)){
                                                                        $this->set_error("upload_destination_error");
                                                                        return FALSE;
                                                                }
                                                        }
                                                
                                                /* Set the finalized image dimensions
                                                 * This sets the image width/height (assuming the
                                                 * file was an image). We use this information
                                                 * in the "data" function.
                                                 */
                                                        $this->set_image_properties($this->upload_path.$this->file_name);
                                                        
                                                //Set current file data to multi_file_upload_data.
                                                $this->set_multi_upload_data();
                                        }
                                        
                                        //Return all file upload data.
                                        return TRUE;
                        }
                }
?>